//////////////////////////////////////////////////
// EASIM project
// easim.win.Frame
// 6/7/2007 6:39 PM
// Cong Liu
// gzcong@gmail.com

// Copyright (C) 2007
// All rights reserved.
// Refer to LICENSE of JIST (Java In Simulation Time) for terms and conditions of use.

package easim.win;

import javax.swing.UIManager;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Toolkit;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.util.HashSet;
import java.util.Properties;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.IOException;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JPopupMenu;
import javax.swing.filechooser.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import jist.swans.misc.Message;
import easim.net.Node;
import easim.net.Field;

public class Frame extends JFrame implements jist.runtime.JistAPI.DoNotRewrite
{

  public static final int NODE_SIZE = 9;
  public static final int LABEL_SIZE = 9;
  public static final long REPAINT_INTERVAL = 100;

  public static final int FIELD_WIDTH = 800;
  public static final int FIELD_HEIGHT = 600;

	public static Frame frame;
	public static NodeFactory nodeFactory;
	public static JPopupMenu popupMenu1;
	public static EditControler.PopupMenu popupMenu2;
	public static Object info;

	public FileMenu fileMenu;
	public ToolsMenu toolsMenu;
	public JMenuBar menuBar;
	public final FieldPane fieldPane;

  static class WindowAdapter1 extends WindowAdapter implements jist.runtime.JistAPI.DoNotRewrite
  {
    public void windowClosing(WindowEvent e) {
      System.exit(0);
    }
  }

	private Frame() {
		this.fieldPane = new FieldPane();
		this.fieldPane.setPreferredSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
		this.setTitle("EASIM");
		this.getContentPane().setLayout(new BorderLayout());
		this.getContentPane().add(fieldPane, BorderLayout.CENTER);

		this.addWindowListener(new WindowAdapter1());

		menuBar = new JMenuBar();
		this.fileMenu = new FileMenu("File", this);
		menuBar.add(this.fileMenu);
    this.toolsMenu = new ToolsMenu("Tools", this);
		menuBar.add(this.toolsMenu);
		this.setJMenuBar(menuBar);

		this.pack();
    int width = this.getWidth();
    int height = this.getHeight();
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension screenSize = toolkit.getScreenSize();
		Rectangle screenRect = new Rectangle(screenSize);
		this.setBounds(screenRect.width - width, 0, width, height);
		this.setVisible(true);

    Field.NODES = 0;

	}

  //////////////////////////////
  // test
  //

  static class FrameThread extends Thread implements jist.runtime.JistAPI.DoNotRewrite
  {
    public void run() {
      frame = new Frame();
      if (nodeFactory == null) {
        frame.fieldPane.nodeFactory = new NodeFactory1();
      }
      else {
        frame.fieldPane.nodeFactory = nodeFactory;
      }
      if (popupMenu1 == null) {
        ((EditControler)frame.fieldPane.controler).popupMenu1 = new PopupMenu1();
      }
      else {
        ((EditControler)frame.fieldPane.controler).popupMenu1 = popupMenu1;
      }
      if (popupMenu2 == null) {
        ((EditControler)frame.fieldPane.controler).popupMenu2 = new PopupMenu2();
      }
      else {
        ((EditControler)frame.fieldPane.controler).popupMenu2 = popupMenu2;
      }
      if (info != null) {
        ((EditControler)frame.fieldPane.controler).info = info;
      }
    }
  }

/*
  static class Node1 extends Node implements jist.runtime.JistAPI.DoNotRewrite
  {
    public easim.mobi.Mobility mobility;
    public Node1(int id) {
      super(id);
      mobility = new easim.mobi.RandomWaypoint(this);
      //mobility = new easim.mobi.Teleport(this);
    }
    public void receive(jist.swans.misc.Message msg, int srcId, int dstId) {
      System.out.println(id + " recv msg (" + srcId + "->" + dstId + ")");
    }
    public void pump(int queueSize, boolean macBusy) {
    }
    public String toString() {
      return getId() + "(" + getX() + "," + getY() + ")";
    }
  }

  static class NodeFactory1 implements easim.win.NodeFactory, jist.runtime.JistAPI.DoNotRewrite
  {
    protected int id = 0;
    public Node create(int id) {
      ++ this.id;
      if (this.id < id) this.id = id;
      return new Node1(this.id);
    }
    public void reset() {
      id = 0;
    }
  }

  static class PopupMenu extends EditControler.PopupMenu 
    implements ActionListener, jist.runtime.JistAPI.DoNotRewrite
  {
    protected Node node;
    public void setNode(Node node) {
      this.node = node;
    }
    public PopupMenu() {
      JMenuItem broadcast = new JMenuItem("Broadcast");
      JMenuItem startMobility = new JMenuItem("Start Mobility");
      JMenuItem stopMobility = new JMenuItem("Stop Mobility");
			broadcast.addActionListener(this);
			startMobility.addActionListener(this);
			stopMobility.addActionListener(this);
			add(broadcast);
			add(startMobility);
			add(stopMobility);
    }
    public void actionPerformed(ActionEvent ev) {
      String cmd = ev.getActionCommand();
      if (cmd.equals("Broadcast")) {
        node.send(Message.NULL, Node.ADDRESS_ANY);
      }
      else if (cmd.equals("Start Mobility")) {
        ((Node1)node).mobility.start();
      }
      else if (cmd.equals("Stop Mobility")) {
        ((Node1)node).mobility.stop();
      }
    }
  }
*/

	private static JFileChooser fileChooser1;

	private static void initFileChooser1() {
		fileChooser1 = new JFileChooser();
		fileChooser1.setCurrentDirectory(new File("."));
    fileChooser1.setDialogTitle("Loading class");
		fileChooser1.setFileFilter(new FileFilter() {
			public boolean accept (File file) {
				return file.getName().toLowerCase().endsWith(".class") || 
					file.isDirectory();
			}
			public String getDescription() {
				return "Java class files";
			}
	    });
	}

	private static void handelException(Exception ex) {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    PrintStream printOut = new PrintStream(output);
    ex.printStackTrace(printOut);
    JOptionPane.showMessageDialog(frame, output.toString(),
      "Exception", JOptionPane.ERROR_MESSAGE);
    //System.exit(0);
  }

	private static Class loadClass(String cname) {
    if (fileChooser1 == null) {
      initFileChooser1();
    }
		fileChooser1.setDialogTitle("Loading class " + cname);
		fileChooser1.rescanCurrentDirectory();
		int result = fileChooser1.showOpenDialog(frame);
		if(result == JFileChooser.APPROVE_OPTION){
			File fp = fileChooser1.getCurrentDirectory();
			String file = fp.getPath() + fp.separator + 
				fileChooser1.getSelectedFile().getName();
      if (! file.endsWith(".class")) return null;
      file = file.substring(0, file.length() - 6);
      file = file.replaceAll("\\\\", "/");
      String currentpath = new File(".").getAbsolutePath();
      if (currentpath.endsWith(".")) {
        currentpath = currentpath.substring(0, currentpath.length() - 1);
      }
      currentpath = currentpath.replaceAll("\\\\", "/");
      //System.out.println("currentpath = " + currentpath);
      //System.out.println("file = " + file);
      if (file.startsWith(currentpath)) {
        file = file.substring(currentpath.length());
      }
      if (file.startsWith("/")) {
        file = file.substring(1);
      }
      file = file.replaceAll("/", ".");
			try {
        return Class.forName(file);
			} catch (Exception ex) {
        handelException(ex);
			}
		}
    return null;
	}

	private static Class loadNodeClass() {
    while (true) {
      try {
        Class c = loadClass("Node");
        if (! Node.class.isAssignableFrom(c)) {
          handelException(new Exception("Not a subclass of easim.net.Node"));
          continue;
        }
        return c;
      } catch (Exception ex) {
        handelException(ex);
        continue;
      }
    }
  }

  public static class NodeFactory1 implements easim.win.NodeFactory, jist.runtime.JistAPI.DoNotRewrite
  {
    public static Class nodeClass;
    protected int id = 0;
    public Node create(int id) {
      ++ this.id;
      if (this.id < id) this.id = id;
      return newNode(this.id);
    }
    public void reset() {
      id = 0;
    }
    public Node newNode(int id) {
      if (nodeClass == null) {
        nodeClass = loadNodeClass();
      }
      try {
        Constructor constructor = nodeClass.getConstructor(new Class[] {int.class});
        Node node = (Node)constructor.newInstance(new Object[] {new Integer(id)});
        return node;
      } catch (Exception ex) {
        handelException(ex);
        System.exit(0);
        return null;
      }
    }
  }

	private static Class loadClassPopupMenu1() {
    while (true) {
      Class c = loadClass("PopupMenu (when right click the panel)");
      if (! javax.swing.JPopupMenu.class.isAssignableFrom(c)) {
        handelException(new Exception("Not a subclass of javax.swing.JPopupMenu"));
        continue;
      }
      return c;
    }
  }

  public static class PopupMenu1 extends javax.swing.JPopupMenu 
    implements ActionListener, jist.runtime.JistAPI.DoNotRewrite
  {
    public PopupMenu1() {
      JMenuItem setClass = new JMenuItem("Set Class");
			setClass.addActionListener(this);
			add(setClass);
    }
    public void actionPerformed(ActionEvent ev) {
      try {
        Class c = loadClassPopupMenu1();
        javax.swing.JPopupMenu popupMenu = (javax.swing.JPopupMenu)c.newInstance();
        ((EditControler)frame.fieldPane.controler).popupMenu1 = popupMenu;
      } catch (Exception ex) {
        handelException(ex);
        System.exit(0);
      }
    }
  }

	private static Class loadClassPopupMenu2() {
    while (true) {
      Class c = loadClass("PopupMenu (when right click a node)");
      if (! EditControler.PopupMenu.class.isAssignableFrom(c)) {
        handelException(new Exception("Not a subclass of EditControler.PopupMenu"));
        continue;
      }
      return c;
    }
  }

  public static class PopupMenu2 extends EditControler.PopupMenu 
    implements ActionListener, jist.runtime.JistAPI.DoNotRewrite
  {
    public void setNode(Node node) {
    }
    public PopupMenu2() {
      JMenuItem setClass = new JMenuItem("Set Class");
			setClass.addActionListener(this);
			add(setClass);
    }
    public void actionPerformed(ActionEvent ev) {
      try {
        Class c = loadClassPopupMenu2();
        EditControler.PopupMenu popupMenu = (EditControler.PopupMenu)c.newInstance();
        ((EditControler)frame.fieldPane.controler).popupMenu2 = popupMenu;
      } catch (Exception ex) {
        handelException(ex);
        System.exit(0);
      }
    }
  }

  public static void main(String[] args) {
    SynControler.start();
    new FrameThread().start();
  }
  /**/

}
